package cn.huoyingkeji.mybitisplus.service.impl;

import cn.huoyingkeji.mybitisplus.service.MybatisplusService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.annotation.TableField;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;

import cn.huoyingkeji.common.domain.DTO;
import cn.huoyingkeji.common.domain.IResultCode;
import cn.huoyingkeji.common.domain.ResultCode;
import cn.huoyingkeji.common.exception.SharpknifeException;
import cn.huoyingkeji.mybitisplus.domain.BaseEntity;
import jakarta.validation.constraints.NotEmpty;
import lombok.extern.slf4j.Slf4j;

import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public abstract class MybatisplusServiceImpl<M extends MPJBaseMapper<T>, T extends BaseEntity, D extends DTO> extends ServiceImpl<M, T> implements MybatisplusService<M,T,D> {


    private Map<String,String> ENTITY_FIELDS = new HashMap<>();


    public MybatisplusServiceImpl() {
    }
    protected final Class<?>[] typeArguments = GenericTypeUtils.resolveTypeArguments(getClass(), MybatisplusServiceImpl.class);


    protected final Class<D> dtoClass = currentDTOClass();

    public Class<D> getDTOClass(){
        return dtoClass;
    }

    @SuppressWarnings("unchecked")
    public Class<D> currentDTOClass(){
        return (Class<D>) this.typeArguments[2];
    }

    public boolean save(T entity) {
        this.resolveEntity(entity);
        return super.save(entity);
    }


    public boolean updateById(T entity) {
        return super.updateById(entity);
    }


    public boolean saveOrUpdate(T entity) {
        return entity.getId() == null ? this.save(entity) : this.updateById(entity);
    }


    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean deleteLogic(@NotEmpty List<Long> ids) {
        LambdaUpdateWrapper<T> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(ids!=null && ids.size()>0,T::getId, ids)
        .set(T::getIsDeleted, 1);
        if(this.baseMapper.update(lambdaUpdateWrapper)>0){
            return true;
        }else{
            log.error("sharpknife===> 删除失败，删除信息为:" + JSONUtil.toJsonStr(ids));
            return false;
        }

        
    }

    public boolean changeStatus(@NotEmpty List<Long> ids, Integer status) {

        LambdaUpdateWrapper<T> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(ids!=null && ids.size()>0,T::getId, ids)
        .set(T::getStatus, status);
        if(this.baseMapper.update(lambdaUpdateWrapper)>0){
            return true;
        }else{
            log.error("sharpknife===> 修改失败，失败信息为:" + JSONUtil.toJsonStr(ids));
            return false;
        }
    }


    private void resolveEntity(T entity) {
        entity.setCreateTime(LocalDateTime.now());
        entity.setIsDeleted(0);
        entity.setStatus(0);
    }



    @Override
    public D add(D dto) throws SharpknifeException {
        T entity = toEntity(dto);
        if(this.save(entity)){
            return toDTO(entity);
        }else{
            log.error("sharpknife===> 添加错误，添加信息为:" + JSONUtil.toJsonStr(dto));
            throw SharpknifeException.build(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public Boolean del(Long id) throws SharpknifeException {
        if(this.removeById(id)){
            return true;
        }
        else{
            log.error("sharpknife===> 删除失败，删除信息为:" + id);
            return false;
        }
    }


    @Override
    public Boolean del(Set<Long> ids) throws SharpknifeException {
        if(this.removeBatchByIds(ids)){
            return true;
        }
        else{
            log.error("sharpknife===> 删除失败，删除信息为:" + JSONUtil.toJsonStr(ids));
            return false;
        }
    }


    @Override
    public Boolean del(D dto) throws SharpknifeException {
        return this.del(dto.getId());
    }


    @Override
    public D update(D dto) throws SharpknifeException {
        T t = toEntity(dto);
        int count = this.baseMapper.updateById(t);
        if(count>0){
            return toDTO(t);
        }else{
            log.error("sharpknife===> 修改错误，修改信息为:" + JSONUtil.toJsonStr(dto));
            throw SharpknifeException.build(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public D details(Long id) throws SharpknifeException {
    
        try {
            MPJLambdaWrapper<T> lambdaWrapper = new MPJLambdaWrapper<>();
            lambdaWrapper.selectAsClass(getEntityClass(), getDTOClass())
            .select("u1.name as createrName")
            .select("u2.name as updateName")
            .leftJoin("t_sys_user u1 on u1.id = t.create_by")
            .leftJoin("t_sys_user u2 on u2.id = t.update_by")
            .eq("t.id", id);
            D dto = this.baseMapper.selectJoinOne(getDTOClass(), lambdaWrapper);
            return dto;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("sharpknife===> 查询错误，信息为:id=" + id);
            throw SharpknifeException.build(new IResultCode() {

                @Override
                public String getMessage() {
                    return e.getMessage();
                }

                @Override
                public int getCode() {
                    return 404;
                } 
            });
        }
       
    }

    @Override
    public List<D> all(D dto) throws SharpknifeException {
        MPJLambdaWrapper<T> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper = this.selectMpjLambdaWrapper(dto, lambdaWrapper);
        return this.baseMapper.selectJoinList(getDTOClass(), lambdaWrapper);
    }


    @Override
    public IPage<D> page(cn.huoyingkeji.common.domain.Page page,D dto) throws SharpknifeException {
        if(null == page){
            page = new cn.huoyingkeji.common.domain.Page();
        }
        MPJLambdaWrapper<T> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper = this.selectMpjLambdaWrapper(dto, lambdaWrapper);
        return this.baseMapper.selectJoinPage(Page.of(page.getCurrent(), page.getSize()), getDTOClass(), lambdaWrapper);
    }

    protected MPJLambdaWrapper<T> selectMpjLambdaWrapper(DTO dto,MPJLambdaWrapper<T> lambdaWrapper){
        if(null != ENTITY_FIELDS && ENTITY_FIELDS.isEmpty()){
            this.getEntotyFields();
        }
        Map<String,Object> map = BeanUtil.beanToMap(dto, true, true);
        if(null != map && !map.isEmpty() && !ENTITY_FIELDS.isEmpty()){
            Set<String> mapKeys = ENTITY_FIELDS.keySet().stream()
                .filter(map.keySet()::contains)
                .collect(Collectors.toSet());
            for (String k : mapKeys) {
                lambdaWrapper.eq( "t." + ENTITY_FIELDS.get(k), map.get(k));
            }
        }
        if(dto != null){
        if(null!=dto.getStartTime() && null != dto.getEndTime()){
            if(dto.getEndTime().isBefore(dto.getStartTime())){
                log.error("sharpknife===> 请求参数错误，开始时间应该早于结束时间，参数信息为:" + JSONUtil.toJsonStr(dto));
               throw  SharpknifeException.build(ResultCode.PARAM_TYPE_ERROR);
            }else{
                lambdaWrapper.between(T::getCreateTime, dto.getStartTime(), dto.getEndTime());
            }
            if(null != dto.getDay() && dto.getDay()>1){
                String subDateSql = "DATE_SUB(CURDATE(), INTERVAL " + dto.getDay() + " DAY)";
                lambdaWrapper.apply("t.create_time >= " + subDateSql);
            }
        }
    }
        lambdaWrapper.selectAsClass(getEntityClass(), getDTOClass())
            .select("u1.name as createrName")
            .select("u2.name as updateName")
            .leftJoin("t_sys_user u1 on u1.id = t.create_by")
            .leftJoin("t_sys_user u2 on u2.id = t.update_by");
        return lambdaWrapper;
    }


    private void getEntotyFields(){
        Field[] fields = getEntityClass().getDeclaredFields();
        for (Field field : fields) {
            String k = StrUtil.toUnderlineCase(field.getName());
           if(field.isAnnotationPresent(TableField.class)){
                TableField[] tableField = field.getAnnotationsByType(TableField.class);
                boolean exist = true;
                String tableFieldName = null;
                for (TableField t : tableField) {
                    tableFieldName = t.value();
                    if(!t.exist()){
                        exist = false;
                        continue;
                    } 
                }
                if(exist){
                   
                    if(null != tableFieldName){
                        ENTITY_FIELDS.put(k, tableFieldName);
                    }else{
                        ENTITY_FIELDS.put(k, k);
                    }
                }
           }else{
                if(!"serialVersionUID".equals(field.getName())){
                    ENTITY_FIELDS.put(k, k); 
                }
           }
        }
    }
}
